/*
 *
 * Copyright (c) 2016-2019 Cisco Systems, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 *   Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 *   Neither the name of the Cisco Systems, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * ssh.c
 *
 * Secure Shell (SSH) awareness for joy
 *
 */

#include <stdio.h>      /* for fprintf()           */
#include <stdlib.h>     /* for malloc, realloc, free */
#include <stdint.h>     /* for uint32_t            */

#ifdef WIN32
# include "Ws2tcpip.h"
# define strtok_r strtok_s
#else
# include <arpa/inet.h>  /* for ntohl()             */
#endif

#include "safe_lib.h"
#include "ssh.h"
#include "utils.h"      /* for enum role */
#include "p2f.h"        /* for zprintf_ ...        */
#include "err.h"        /* for logging             */

/*
 *
 * \brief Copy a json-printable string from the source buffer to the
 *        destination buffer.
 *
 * \param buf Destination buffer.
 * \param buflen Maximum length of the destination buffer.
 * \param data Source buffer.
 * \param datalen Length of the source buffer.
 *
 */
static void copy_printable_string(char *buf,
                                  unsigned int buflen,
                                  const char *data,
                                  unsigned int datalen) {
    while (buflen-- && datalen--) {
        /* json constraints */
	    if (!isprint(*data) || *data == '\"' || *data == '\\' || *data <= 0x1f) {
	        break;
	    }
	    *buf++ = *data++;
    }

    *buf = 0; /* null terminate buffer */
}

/*
 *
 * \brief Find a substring in a buffer of a given length, and return a pointer
 * to the first substring match. The substring search will terminate upon
 * encountering a null byte or reaching the end of the buffer.
 *
 * \param buf The buffer to be searched.
 * \param buflen The length of the buffer.
 * \param sub The substring to search for.
 * \param sublen The length of the substring.
 *
 * \return A pointer to the first substring match, or NULL if not found.
 *
 */
static const char * memsearch(const char *buf, 
			      const unsigned int buflen, 
			      const char *sub, 
			      const unsigned int sublen) {
    unsigned int bufidx;
    int cmp_rc;

    for (bufidx = 0; bufidx < buflen; bufidx++) {
        if ((bufidx + sublen > buflen) || buf[bufidx] == '\0') {
            break;
        }
        if ((EOK == memcmp_s(buf+bufidx, sublen, sub, sublen, &cmp_rc)) && cmp_rc == 0) {
            return buf+bufidx;
        }
    }

    return NULL;
}

/*
 * A vector is contains a pointer to a string of bytes of a specified length.
 */
struct vector {
    unsigned int len;
    char *bytes;
};

/*
 *
 * \brief Initialize vector struct.
 *
 * \param vector Pointer to the vector to be initialized.
 *
 */
static void vector_init(struct vector *vector) {
    vector->len = 0;
    vector->bytes = NULL;
}

/*
 *
 * \brief Free a vector, setting it to the state just after a call to vector_init.
 *
 * \param vector Pointer to the vector to free.
 *
 */
static void vector_free(struct vector *vector) {
    if (vector->bytes != NULL) {
        free(vector->bytes);
    }
    vector_init(vector);
}

/*
 *
 * \brief Set the vector contents to the specified data, freeing the previous
 * vector contents. If the previous vector contents overlap in memory with the
 * new vector contents, the behavior is still defined since the free occurs
 * after the copy.
 *
 * \param vector Pointer to the vector to be set.
 * \param data Pointer to byte array to be copied.
 * \param len Length of the byte array to be copied.
 *
 */
static void vector_set(struct vector *vector,
                       const char *data,
                       unsigned int len) {
    char *tmpptr = NULL;

    tmpptr = malloc(len);
    if (tmpptr == NULL) {
        return;
    }
    memcpy_s(tmpptr, len, data, len);
    vector_free(vector); /* does nothing if already empty */
    vector->bytes = tmpptr;
    vector->len = len;
}

/*
 *
 * \brief Append the specified data to the current vector contents, even if the
 * vector is currently empty.
 *
 * \param vector Pointer to the vector to be appended to.
 * \param data Pointer to byte array to be appended.
 * \param len Length of the byte array to be appended.
 *
 */
static void vector_append(struct vector *vector,
                          const char *data,
                          unsigned int len) {

    vector->bytes = realloc(vector->bytes, vector->len + len);
    if (vector->bytes == NULL) {
        return;
    }
    memcpy_s(vector->bytes + vector->len, len, data, len);
    vector->len += len;
}

/*
 *
 * \brief Allocate and return a pointer to a string representation of a vector.
 * This string must later be freed to avoid a memory leak.
 *
 * \param vector Pointer to the vector to stringify.
 *
 * \return A pointer to a string representation of the vector.
 */
static char *vector_string(struct vector *vector) {
    char *s;

    s = malloc(vector->len+1);
    if (s == NULL) {
        return NULL;
    }
    if (vector->len > 0) {
        copy_printable_string(s, vector->len+1, vector->bytes, vector->len);
    } else {
        s[0] = 0;
    }

    return s;
}

/*
 * from http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml
 */
enum ssh_msg_type {
    SSH_MSG_DISCONNECT                  = 1,
    SSH_MSG_IGNORE                      = 2,
    SSH_MSG_UNIMPLEMENTED               = 3,
    SSH_MSG_DEBUG                       = 4,
    SSH_MSG_SERVICE_REQUEST             = 5,
    SSH_MSG_SERVICE_ACCEPT              = 6,
    SSH_MSG_KEXINIT                     = 20,
    SSH_MSG_NEWKEYS                     = 21,
    SSH_MSG_USERAUTH_REQUEST            = 50,
    SSH_MSG_USERAUTH_FAILURE            = 51,
    SSH_MSG_USERAUTH_SUCCESS            = 52,
    SSH_MSG_USERAUTH_BANNER             = 53,
    SSH_MSG_USERAUTH_INFO_REQUEST       = 60,
    SSH_MSG_USERAUTH_INFO_RESPONSE      = 61,
    SSH_MSG_GLOBAL_REQUEST              = 80,
    SSH_MSG_REQUEST_SUCCESS             = 81,
    SSH_MSG_REQUEST_FAILURE             = 82,
    SSH_MSG_CHANNEL_OPEN                = 90,
    SSH_MSG_CHANNEL_OPEN_CONFIRMATION   = 91,
    SSH_MSG_CHANNEL_OPEN_FAILURE        = 92,
    SSH_MSG_CHANNEL_WINDOW_ADJUST       = 93,
    SSH_MSG_CHANNEL_DATA                = 94,
    SSH_MSG_CHANNEL_EXTENDED_DATA       = 95,
    SSH_MSG_CHANNEL_EOF                 = 96,
    SSH_MSG_CHANNEL_CLOSE               = 97,
    SSH_MSG_CHANNEL_REQUEST             = 98,
    SSH_MSG_CHANNEL_SUCCESS             = 99,
    SSH_MSG_CHANNEL_FAILURE             = 100
};

/*
 * from RFC 4253:
 *   Each packet is in the following format:
 *
 *    uint32    packet_length
 *    byte      padding_length
 *    byte[n1]  payload; n1 = packet_length - padding_length - 1
 *    byte[n2]  random padding; n2 = padding_length
 *    byte[m]   mac (Message Authentication Code - MAC); m = mac_length
 *
 */
#ifdef WIN32

#define PACKED
#pragma pack(push,1)

struct ssh_packet {
    uint32_t      packet_length;
    unsigned char padding_length;
    unsigned char payload;
} PACKED;

#pragma pack(pop)
#undef PACKED

#else

struct ssh_packet {
    uint32_t      packet_length;
    unsigned char padding_length;
    unsigned char payload;
} __attribute__((__packed__));

#endif

static unsigned int ssh_packet_parse(const char *pkt,
                                     unsigned int datalen,
                                     unsigned char *msg_code,
                                     unsigned int *total_length) {
    const struct ssh_packet *ssh_packet = (const struct ssh_packet *)pkt;
    uint32_t length;

    if (datalen < sizeof(struct ssh_packet)) {
    return 0;
    }

    length = ntohl(ssh_packet->packet_length);
    if (length > MAX_SSH_PAYLOAD_LEN) {
    return 0;   /* indicate parse error */
    }
    *total_length = length + 4;
    *msg_code = ssh_packet->payload;

    /* robustness check */
    length -= ssh_packet->padding_length;
    if (length > MAX_SSH_PAYLOAD_LEN) {
      return 0;
    }

    return length;
}

static unsigned int decode_uint32(const char *data) {
    const uint32_t *x = (const uint32_t *)data;

    return ntohl(*x);
}

static joy_status_e decode_ssh_vector(const char **dataptr,
                                     unsigned int *datalen,
                                     struct vector *vector,
                                     unsigned int maxlen) {
    const char *data = *dataptr;
    unsigned int length;

    if (*datalen < 4) {
        joy_log_info("wanted %u, only have %u", 4, *datalen);
        return failure;
    }

    length = decode_uint32(data);
    *datalen -= 4;

    if (length > *datalen) {
        joy_log_info("wanted %u, only have %u", length, *datalen);
        return failure;
    }

    data += 4;

    /* robustness check */
    if (length > maxlen) {
        return failure;
    }

    vector_set(vector, data, length);

    data += length;
    *datalen -= length;
    *dataptr = data;
    return ok;
}

/*
 * from RFC 4253 Section 7.1
 *
 *    Key exchange begins by each side sending the following packet:
 *
 *    byte         SSH_MSG_KEXINIT
 *    byte[16]     cookie (random bytes)
 *    name-list    kex_algorithms
 *    name-list    server_host_key_algorithms
 *    name-list    encryption_algorithms_client_to_server
 *    name-list    encryption_algorithms_server_to_client
 *    name-list    mac_algorithms_client_to_server
 *    name-list    mac_algorithms_server_to_client
 *    name-list    compression_algorithms_client_to_server
 *    name-list    compression_algorithms_server_to_client
 *    name-list    languages_client_to_server
 *    name-list    languages_server_to_client
 *    boolean      first_kex_packet_follows
 *    uint32       0 (reserved for future extension)
 *
 */
static void ssh_parse_kexinit(struct ssh *ssh,
                              const char *data,
                              unsigned int datalen) {
    /* robustness check */
    if (ssh->kex_algos->len != 0) {
        return;
    }

    /* copy the cookie  */
    if (datalen < 16) {
        return;
    }
    memcpy_s(ssh->cookie, 16, data, 16);
    data += 16;
    datalen -= 16;

    /* copy all name-list strings */
    if (decode_ssh_vector(&data, &datalen, ssh->kex_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->s_host_key_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->c_encryption_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->s_encryption_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->c_mac_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->s_mac_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->c_comp_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->s_comp_algos, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->c_languages, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    if (decode_ssh_vector(&data, &datalen, ssh->s_languages, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
}

static void ssh_get_kex_algo(struct ssh *cli,
                             struct ssh *srv) {
    char *cli_copy, *srv_copy;
    const char *cli_algo, *srv_algo;
    char *cli_ptr, *srv_ptr;
    const char *sep = ",";
    unsigned len;
    int found = 0;
    int cmp_ind;

    cli_copy = vector_string(cli->kex_algos);
    srv_copy = vector_string(srv->kex_algos);

    for(cli_algo = strtok_r(cli_copy, sep, &cli_ptr); cli_algo && !found; cli_algo = strtok_r(NULL, sep, &cli_ptr)) {
        for(srv_algo = strtok_r(srv_copy, sep, &srv_ptr); srv_algo && !found; srv_algo = strtok_r(NULL, sep, &srv_ptr)) {
            if ((strcmp_s(srv_algo, 200, cli_algo, &cmp_ind) == EOK) && cmp_ind == 0 ) {
                found = 1;
                break;
            }
        }
        /* prevent increment in outer loop */
        if (found) break;
    }

    if (!found) {
        cli_algo = "";
    }

    len = strnlen_s(cli_algo, 100);//tbd
    cli->kex_algo = malloc(len+1);
    strncpy_s(cli->kex_algo, len+1, cli_algo, len); /* strncpy will null-terminate the string */
    srv->kex_algo = malloc(len+1);
    strncpy_s(srv->kex_algo, len+1, cli_algo, len); /* strncpy will null-terminate the string */

    free(cli_copy);
    free(srv_copy);
}

/*
 * from RFC 4253, Section 8
 * decode e
 */
static void ssh_parse_kexdh_init(struct ssh *ssh,
                                 const char *data,
                                 unsigned int datalen) {
    /* copy client key exchange value */
    if (decode_ssh_vector(&data, &datalen, ssh->c_kex, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    ssh->role = role_client;
}

static void ssh_parse_kexdh_reply(struct ssh *ssh,
                                  const char *data,
                                  unsigned int datalen) {
    const char *tmpptr;
    unsigned int tmplen;

    /* copy server public host key and certificates (K_S) */
    if (decode_ssh_vector(&data, &datalen, ssh->s_hostkey, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    /* copy host key type */
    tmpptr = ssh->s_hostkey->bytes;
    tmplen = ssh->s_hostkey->len;
    if (decode_ssh_vector(&tmpptr, &tmplen, ssh->s_hostkey_type, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    /* copy server key exchange value */
    if (decode_ssh_vector(&data, &datalen, ssh->s_kex, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }
    /* copy signature */
    if (decode_ssh_vector(&data, &datalen, ssh->s_signature, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    /* copy signature type */
    tmpptr = ssh->s_signature->bytes;
    tmplen = ssh->s_signature->len;
    if (decode_ssh_vector(&tmpptr, &tmplen, ssh->s_signature_type, MAX_SSH_STRING_LEN) == failure) {
        return;
    }

    ssh->role = role_server;
}

static void ssh_parse_kex_dh_gex_request(struct ssh *ssh,
                                         const char *data,
                                         unsigned int datalen) {
    if (datalen < 4) {
        return;
    }
    ssh->c_gex_min = decode_uint32(data);
    if (datalen < 12) {
        /* old format */
        ssh->c_gex_n = ssh->c_gex_max = ssh->c_gex_min;
    } else {
        ssh->c_gex_n = decode_uint32(data+4);
        ssh->c_gex_max = decode_uint32(data+8);
    }
}

static void ssh_parse_kex_dh_gex_group(struct ssh *ssh,
                                       const char *data,
                                       unsigned int datalen) {
    /* copy safe prime p */
    if (decode_ssh_vector(&data, &datalen, ssh->s_gex_p, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    /* copy generator g */
    if (decode_ssh_vector(&data, &datalen, ssh->s_gex_g, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }
}

/*
 * from RFC 4432, Section 4
 */
static void ssh_parse_kexrsa_pubkey(struct ssh *ssh,
                                    const char *data,
                                    unsigned int datalen) {
    const char *tmpptr;
    unsigned int tmplen;

    /* copy server public host key and certificates (K_S) */
    if (decode_ssh_vector(&data, &datalen, ssh->s_hostkey, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    /* copy host key type */
    tmpptr = ssh->s_hostkey->bytes;
    tmplen = ssh->s_hostkey->len;
    if (decode_ssh_vector(&tmpptr, &tmplen, ssh->s_hostkey_type, MAX_SSH_STRING_LEN) == failure) {
        return;
    }
    /* copy K_T, the transient RSA public key */
    if (decode_ssh_vector(&data, &datalen, ssh->s_kex, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    ssh->role = role_server;
}

static void ssh_parse_kexrsa_secret(struct ssh *ssh,
                                    const char *data,
                                    unsigned int datalen) {
    /* copy RSA-encrypted secret */
    if (decode_ssh_vector(&data, &datalen, ssh->c_kex, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }

    ssh->role = role_client;
}

static void ssh_parse_kexrsa_done(struct ssh *ssh,
                                  const char *data,
                                  unsigned int datalen) {
    const char *tmpptr;
    unsigned int tmplen;

    /* copy signature */
    if (decode_ssh_vector(&data, &datalen, ssh->s_signature, MAX_SSH_PAYLOAD_LEN) == failure) {
        return;
    }
    /* copy signature type */
    tmpptr = ssh->s_signature->bytes;
    tmplen = ssh->s_signature->len;
    if (decode_ssh_vector(&tmpptr, &tmplen, ssh->s_signature_type, MAX_SSH_STRING_LEN) == failure) {
        return;
    }

    ssh->role = role_server;
}

/*
 * from https://tools.ietf.org/html/rfc4419
 */
#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD  30
#define SSH_MSG_KEX_DH_GEX_REQUEST      34
#define SSH_MSG_KEX_DH_GEX_GROUP        31
#define SSH_MSG_KEX_DH_GEX_INIT         32
#define SSH_MSG_KEX_DH_GEX_REPLY        33
#define ssh_parse_kex_dh_gex_init ssh_parse_kexdh_init
#define ssh_parse_kex_dh_gex_reply ssh_parse_kexdh_reply
static void ssh_gex_kex(struct ssh *cli,
                        struct ssh *srv) {

    if (cli->kex_msgs_len > 0 && (cli->kex_msgs[0].msg_code == SSH_MSG_KEX_DH_GEX_REQUEST_OLD
                || cli->kex_msgs[0].msg_code == SSH_MSG_KEX_DH_GEX_REQUEST)) {
        ssh_parse_kex_dh_gex_request(cli, cli->kex_msgs[0].data->bytes, cli->kex_msgs[0].data->len);
    }
    if (srv->kex_msgs_len > 0 && srv->kex_msgs[0].msg_code == SSH_MSG_KEX_DH_GEX_GROUP) {
        ssh_parse_kex_dh_gex_group(srv, srv->kex_msgs[0].data->bytes, srv->kex_msgs[0].data->len);
    }
    if (cli->kex_msgs_len > 1 && cli->kex_msgs[1].msg_code == SSH_MSG_KEX_DH_GEX_INIT) {
        ssh_parse_kex_dh_gex_init(cli, cli->kex_msgs[1].data->bytes, cli->kex_msgs[1].data->len);
    }
    if (srv->kex_msgs_len > 1 && srv->kex_msgs[1].msg_code == SSH_MSG_KEX_DH_GEX_REPLY) {
        ssh_parse_kex_dh_gex_reply(srv, srv->kex_msgs[1].data->bytes, srv->kex_msgs[1].data->len);
    }
}

/*
 * from https://tools.ietf.org/html/rfc4253
 */
#define SSH_MSG_KEXDH_INIT  30
#define SSH_MSG_KEXDH_REPLY 31
static void ssh_dh_kex(struct ssh *cli,
                       struct ssh *srv) {

    if (cli->kex_msgs_len > 0 && cli->kex_msgs[0].msg_code == SSH_MSG_KEXDH_INIT) {
        ssh_parse_kexdh_init(cli, cli->kex_msgs[0].data->bytes, cli->kex_msgs[0].data->len);
    }
    if (srv->kex_msgs_len > 0 && srv->kex_msgs[0].msg_code == SSH_MSG_KEXDH_REPLY) {
        ssh_parse_kexdh_reply(srv, srv->kex_msgs[0].data->bytes, srv->kex_msgs[0].data->len);
    }
}

/*
 * from https://tools.ietf.org/html/rfc4462
 */
#define SSH_MSG_KEXGSS_INIT                       30
#define SSH_MSG_KEXGSS_CONTINUE                   31
#define SSH_MSG_KEXGSS_COMPLETE                   32
#define SSH_MSG_KEXGSS_HOSTKEY                    33
#define SSH_MSG_KEXGSS_ERROR                      34
#define SSH_MSG_KEXGSS_GROUPREQ                   40
#define SSH_MSG_KEXGSS_GROUP                      41
static void ssh_gss_dh_kex(struct ssh *cli,
                           struct ssh *srv) {
    /* sanity check */
    if ((cli == NULL) || (srv == NULL)) {
        return;
    }

    /* TODO */
    return;
}

static void ssh_gss_gex_kex(struct ssh *cli,
                            struct ssh *srv) {
    /* sanity check */
    if ((cli == NULL) || (srv == NULL)) {
        return;
    }

    /* TODO */
    return;
}

/*
 * from https://tools.ietf.org/html/rfc4432
 */
#define SSH_MSG_KEXRSA_PUBKEY  30
#define SSH_MSG_KEXRSA_SECRET  31
#define SSH_MSG_KEXRSA_DONE    32
static void ssh_rsa_kex(struct ssh *cli,
                        struct ssh *srv) {

    if (srv->kex_msgs_len > 0 && srv->kex_msgs[0].msg_code == SSH_MSG_KEXRSA_PUBKEY) {
        ssh_parse_kexrsa_pubkey(srv, srv->kex_msgs[0].data->bytes, srv->kex_msgs[0].data->len);
    }
    if (cli->kex_msgs_len > 0 && cli->kex_msgs[0].msg_code == SSH_MSG_KEXRSA_SECRET) {
        ssh_parse_kexrsa_secret(cli, cli->kex_msgs[0].data->bytes, cli->kex_msgs[0].data->len);
    }
    if (srv->kex_msgs_len > 1 && srv->kex_msgs[1].msg_code == SSH_MSG_KEXRSA_DONE) {
        ssh_parse_kexrsa_done(srv, srv->kex_msgs[1].data->bytes, srv->kex_msgs[1].data->len);
    }
}

/*
 * from https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml
 */
static void ssh_process(struct ssh *cli,
                        struct ssh *srv) {

    if (cli == NULL || srv == NULL) {
        return;
    }

    ssh_get_kex_algo(cli, srv);

    /* process key exchange messages */
    if (strstr(cli->kex_algo, "diffie-hellman-group-exchange-sha1")
            || strstr(cli->kex_algo, "diffie-hellman-group-exchange-sha256")) {
        ssh_gex_kex(cli, srv);
    } else if (strstr(cli->kex_algo, "diffie-hellman-group1-sha1")
            || strstr(cli->kex_algo, "diffie-hellman-group14-sha1")
            || strstr(cli->kex_algo, "ecdh-sha2-")
            || strstr(cli->kex_algo, "ecmqv-sha2-")
            || strstr(cli->kex_algo, "curve25519-sha256")) {
        ssh_dh_kex(cli, srv);
    } else if (strstr(cli->kex_algo, "gss-group1-sha1-")
            || strstr(cli->kex_algo, "gss-group14-sha1-")) {
        ssh_gss_dh_kex(cli, srv);
    } else if (strstr(cli->kex_algo, "gss-gex-sha1-")) {
        ssh_gss_gex_kex(cli, srv);
    } else if (strstr(cli->kex_algo, "rsa1024-sha1")
            || strstr(cli->kex_algo, "rsa2048-sha256")) {
        ssh_rsa_kex(cli, srv);
    } else {
        return;
    }
}

/**
 *
 * \brief Initialize the memory of SSH struct.
 *
 * \param ssh_handle contains ssh structure to initialize
 *
 * \return none
 */
inline void ssh_init(struct ssh **ssh_handle) {
    int i;

    if (*ssh_handle != NULL) {
        ssh_delete(ssh_handle);
    }

    *ssh_handle = calloc(1, sizeof(struct ssh));
    if (*ssh_handle == NULL) {
        /* Allocation failed */
        joy_log_err("malloc failed");
        return;
    }

    (*ssh_handle)->buffer                 = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->buffer);
    (*ssh_handle)->kex_algos              = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->kex_algos);
    (*ssh_handle)->s_host_key_algos       = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_host_key_algos);
    (*ssh_handle)->c_encryption_algos     = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->c_encryption_algos);
    (*ssh_handle)->s_encryption_algos     = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_encryption_algos);
    (*ssh_handle)->c_mac_algos            = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->c_mac_algos);
    (*ssh_handle)->s_mac_algos            = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_mac_algos);
    (*ssh_handle)->c_comp_algos           = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->c_comp_algos);
    (*ssh_handle)->s_comp_algos           = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_comp_algos);
    (*ssh_handle)->c_languages            = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->c_languages);
    (*ssh_handle)->s_languages            = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_languages);
    (*ssh_handle)->s_hostkey_type         = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_hostkey_type);
    (*ssh_handle)->s_signature_type       = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_signature_type);
    (*ssh_handle)->c_kex                  = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->c_kex);
    (*ssh_handle)->s_kex                  = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_kex);
    (*ssh_handle)->s_hostkey              = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_hostkey);
    (*ssh_handle)->s_signature            = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_signature);
    (*ssh_handle)->s_gex_p                = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_gex_p);
    (*ssh_handle)->s_gex_g                = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->s_gex_g);
    for (i = 0; i < MAX_SSH_KEX_MESSAGES; ++i) {
        (*ssh_handle)->kex_msgs[i].data   = malloc(sizeof(struct vector)); vector_init((*ssh_handle)->kex_msgs[i].data);
    }
}

void ssh_update(struct ssh *ssh,
        const struct pcap_pkthdr *header,
        const void *data,
        unsigned int len,
        unsigned int report_ssh) {
    unsigned int length;
    unsigned int total_length;
    unsigned char msg_code;
    const char *data_ptr = (const char *)data;
    const char *tmpptr;

    if (len == 0) {
        return;    /* skip zero-length messages */
    }

    /* sanity check */
    if (ssh == NULL) {
        return;
    }

    joy_log_debug("ssh[%p],header[%p],data[%p],len[%d],report[%d]",
            ssh,header,data,len,report_ssh);

    if (report_ssh) {

    if (ssh->newkeys) {
        return; /* do not attempt to parse encrypted data */
    } else {
        ssh->unencrypted++;
    }

    /* append application-layer data to buffer */
    vector_append(ssh->buffer, data_ptr, len);
    data_ptr = ssh->buffer->bytes;
    len = ssh->buffer->len;

    if (ssh->role == role_unknown) {
        /*
         * RFC 4253:
         * The server MAY send other lines of data before sending the version
         * string. Each line SHOULD be terminated by a Carriage Return and Line
         * Feed.  Such lines MUST NOT begin with "SSH-".
         */

        /* skip to version message */
        if ((tmpptr = memsearch(data_ptr, len, "SSH-", 4))) {
            len -= (tmpptr-data_ptr);
            data_ptr = tmpptr;
            copy_printable_string(ssh->protocol, sizeof(ssh->protocol), data_ptr, len);
        } else {
            return;
        }

        /* skip past version message */
        if ((tmpptr = memsearch(data_ptr, len, "\n", 1))) {
            tmpptr += 1; /* skip past the "\n" */
            len -= (tmpptr-data_ptr);
            data_ptr = tmpptr;
        } else {
            return;
        }
        ssh->role = role_client; /* ? */
    }

    while(len > 0) { /* parse all SSH packets in buffer */
        length = ssh_packet_parse(data_ptr, len, &msg_code, &total_length);
        if (length == 0 || total_length > len) {
            /* unable to parse SSH packet */
            break;
        }
        switch (msg_code) {
        case SSH_MSG_KEXINIT:

            ssh_parse_kexinit(ssh, data_ptr + sizeof(struct ssh_packet), length);
            break;
        case SSH_MSG_NEWKEYS:

            ssh->newkeys = 1;
            break;
        default:

            /* key exchange specific messages */
            if (msg_code >= 30 && msg_code <= 49) {
                if (ssh->kex_msgs_len < MAX_SSH_KEX_MESSAGES) {
                    ssh->kex_msgs[ssh->kex_msgs_len].msg_code = msg_code;
                    vector_set(ssh->kex_msgs[ssh->kex_msgs_len].data, data_ptr + sizeof(struct ssh_packet), length);
                    ssh->kex_msgs_len++;
                }
            }
            break;
        }

        /* skip to the next message in buffer */
        len -= total_length;
        data_ptr += total_length;
    }

    /* update or free buffer */
    if (len > 0) {
        vector_set(ssh->buffer, data_ptr, len);
    } else {
        vector_free(ssh->buffer);
    }

    }

}

void ssh_print_json(const struct ssh *x1,
                    const struct ssh *x2,
                    zfile f) {

    struct ssh *cli = NULL, *srv = NULL;
    char *ptr;

    if (x1->role == role_unknown) {
        return;
    }
    if (x1->role == role_client) {
        cli = (struct ssh*)x1;
        srv = (struct ssh*)x2;
    } else { // x1->role == role_server
        srv = (struct ssh*)x1;
    }
    ssh_process(cli, srv);
    zprintf(f, ",\"ssh\":{");
    if (cli != NULL) {
        zprintf(f, "\"cli\":{");
        zprintf(f, "\"protocol\":\"%s\"", cli->protocol);
        if (cli->cookie[0] != 0) {
            zprintf(f, ",\"cookie\":");
            zprintf_raw_as_hex(f, cli->cookie, sizeof(cli->cookie));
        }
        ptr = vector_string(cli->kex_algos); zprintf(f, ",\"kex_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->s_host_key_algos); zprintf(f, ",\"s_host_key_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->c_encryption_algos); zprintf(f, ",\"c_encryption_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->s_encryption_algos); zprintf(f, ",\"s_encryption_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->c_mac_algos); zprintf(f, ",\"c_mac_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->s_mac_algos); zprintf(f, ",\"s_mac_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->c_comp_algos); zprintf(f, ",\"c_comp_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->s_comp_algos); zprintf(f, ",\"s_comp_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->c_languages); zprintf(f, ",\"c_languages\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(cli->s_languages); zprintf(f, ",\"s_languages\":\"%s\"", ptr); free(ptr);
        if (cli->kex_algo != NULL) {
        zprintf(f, ",\"kex_algo\":\"%s\"", cli->kex_algo);
        }
        if (cli->c_kex->len > 0) {
        zprintf(f, ",\"c_kex\":");
        zprintf_raw_as_hex(f, (unsigned char*)cli->c_kex->bytes, cli->c_kex->len);
        }
        zprintf(f, ",\"newkeys\":\"%s\"", cli->newkeys? "true": "false");
        zprintf(f, ",\"unencrypted\":%d", cli->unencrypted);
        zprintf(f, "}");
    }
    if (srv != NULL) {
        if (cli != NULL) {
            zprintf(f, ",");
        }
        zprintf(f, "\"srv\":{");
        zprintf(f, "\"protocol\":\"%s\"", srv->protocol);
        if (srv->cookie[0] != 0) {
            zprintf(f, ",\"cookie\":");
            zprintf_raw_as_hex(f, srv->cookie, sizeof(srv->cookie));
        }
        ptr = vector_string(srv->kex_algos); zprintf(f, ",\"kex_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->s_host_key_algos); zprintf(f, ",\"s_host_key_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->c_encryption_algos); zprintf(f, ",\"c_encryption_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->s_encryption_algos); zprintf(f, ",\"s_encryption_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->c_mac_algos); zprintf(f, ",\"c_mac_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->s_mac_algos); zprintf(f, ",\"s_mac_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->c_comp_algos); zprintf(f, ",\"c_comp_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->s_comp_algos); zprintf(f, ",\"s_comp_algos\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->c_languages); zprintf(f, ",\"c_languages\":\"%s\"", ptr); free(ptr);
        ptr = vector_string(srv->s_languages); zprintf(f, ",\"s_languages\":\"%s\"", ptr); free(ptr);
        if (srv->s_hostkey->len > 0) {
        ptr = vector_string(srv->s_hostkey_type); zprintf(f, ",\"s_hostkey_type\":\"%s\"", ptr); free(ptr);
        zprintf(f, ",\"s_hostkey\":");
        zprintf_raw_as_hex(f, (unsigned char*)srv->s_hostkey->bytes, srv->s_hostkey->len);
        }
        if (srv->s_signature->len > 0) {
        ptr = vector_string(srv->s_signature_type); zprintf(f, ",\"s_signature_type\":\"%s\"", ptr); free(ptr);
        zprintf(f, ",\"s_signature\":");
        zprintf_raw_as_hex(f, (unsigned char*)srv->s_signature->bytes, srv->s_signature->len);
        }
        if (srv->kex_algo != NULL) {
        zprintf(f, ",\"kex_algo\":\"%s\"", srv->kex_algo);
        }
        if (srv->s_kex->len > 0) {
        zprintf(f, ",\"s_kex\":");
        zprintf_raw_as_hex(f, (unsigned char*)srv->s_kex->bytes, srv->s_kex->len);
        }
        if (srv->s_gex_p->len > 0 && srv->s_gex_g->len > 0) {
        zprintf(f, ",\"s_gex_p\":");
        zprintf_raw_as_hex(f, (unsigned char*)srv->s_gex_p->bytes, srv->s_gex_p->len);
        zprintf(f, ",\"s_gex_g\":");
        zprintf_raw_as_hex(f, (unsigned char*)srv->s_gex_g->bytes, srv->s_gex_g->len);
        }
        zprintf(f, ",\"newkeys\":\"%s\"", srv->newkeys? "true": "false");
        zprintf(f, ",\"unencrypted\":%d", srv->unencrypted);
        zprintf(f, "}");
    }
    zprintf(f, "}");
}

/**
 *
 * \brief Delete the memory of SSH struct.
 *
 * \param ssh_handle contains ssh structure to delete
 *
 * \return none
 */
void ssh_delete(struct ssh **ssh_handle) {
    int i;
    struct ssh *ssh = *ssh_handle;

    if (ssh == NULL) {
        return;
    }

    if (ssh->kex_algo != NULL) {
        free(ssh->kex_algo);
    }
    vector_free(ssh->buffer);             free(ssh->buffer);
    vector_free(ssh->kex_algos);          free(ssh->kex_algos);
    vector_free(ssh->s_host_key_algos);   free(ssh->s_host_key_algos);
    vector_free(ssh->c_encryption_algos); free(ssh->c_encryption_algos);
    vector_free(ssh->s_encryption_algos); free(ssh->s_encryption_algos);
    vector_free(ssh->c_mac_algos);        free(ssh->c_mac_algos);
    vector_free(ssh->s_mac_algos);        free(ssh->s_mac_algos);
    vector_free(ssh->c_comp_algos);       free(ssh->c_comp_algos);
    vector_free(ssh->s_comp_algos);       free(ssh->s_comp_algos);
    vector_free(ssh->c_languages);        free(ssh->c_languages);
    vector_free(ssh->s_languages);        free(ssh->s_languages);
    vector_free(ssh->s_hostkey_type);     free(ssh->s_hostkey_type);
    vector_free(ssh->s_signature_type);   free(ssh->s_signature_type);
    vector_free(ssh->c_kex);              free(ssh->c_kex);
    vector_free(ssh->s_kex);              free(ssh->s_kex);
    vector_free(ssh->s_hostkey);          free(ssh->s_hostkey);
    vector_free(ssh->s_signature);        free(ssh->s_signature);
    vector_free(ssh->s_gex_p);            free(ssh->s_gex_p);
    vector_free(ssh->s_gex_g);            free(ssh->s_gex_g);
    for (i = 0; i < MAX_SSH_KEX_MESSAGES; ++i) {
        vector_free(ssh->kex_msgs[i].data); free(ssh->kex_msgs[i].data);
    }

    /* Free the memory and set to NULL */
    free(ssh);
    *ssh_handle = NULL;
}

static int ssh_test_handshake(void) {
    struct ssh *cli = NULL;
    struct ssh *srv = NULL;
    int num_fails = 0;
    int cmp_ind;

    char c_protocol[] = { /* Client Protocol */
        0x53, 0x53, 0x48, 0x2d, 0x32, 0x2e, 0x30, 0x2d,
        0x4f, 0x70, 0x65, 0x6e, 0x53, 0x53, 0x48, 0x5f,
        0x37, 0x2e, 0x34, 0x70, 0x31, 0x20, 0x44, 0x65,
        0x62, 0x69, 0x61, 0x6e, 0x2d, 0x31, 0x30, 0x0d,
        0x0a };
    char s_protocol[] = { /* Server Protocol */
        0x53, 0x53, 0x48, 0x2d, 0x32, 0x2e, 0x30, 0x2d,
        0x4f, 0x70, 0x65, 0x6e, 0x53, 0x53, 0x48, 0x5f,
        0x36, 0x2e, 0x36, 0x2e, 0x31, 0x70, 0x31, 0x20,
        0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x32,
        0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x32, 0x2e,
        0x38, 0x0d, 0x0a };
    char c_kexinit[] = { /* Client Key Exchange Init */
        0x00, 0x00, 0x05, 0x94, 0x0b, 0x14, 0x40, 0xe0,
        0x2f, 0x55, 0xcc, 0x5b, 0x12, 0x19, 0x27, 0x9b,
        0x12, 0x40, 0x7a, 0xd8, 0xec, 0x66, 0x00, 0x00,
        0x01, 0x30, 0x63, 0x75, 0x72, 0x76, 0x65, 0x32,
        0x35, 0x35, 0x31, 0x39, 0x2d, 0x73, 0x68, 0x61,
        0x32, 0x35, 0x36, 0x2c, 0x63, 0x75, 0x72, 0x76,
        0x65, 0x32, 0x35, 0x35, 0x31, 0x39, 0x2d, 0x73,
        0x68, 0x61, 0x32, 0x35, 0x36, 0x40, 0x6c, 0x69,
        0x62, 0x73, 0x73, 0x68, 0x2e, 0x6f, 0x72, 0x67,
        0x2c, 0x65, 0x63, 0x64, 0x68, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70,
        0x32, 0x35, 0x36, 0x2c, 0x65, 0x63, 0x64, 0x68,
        0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d, 0x6e, 0x69,
        0x73, 0x74, 0x70, 0x33, 0x38, 0x34, 0x2c, 0x65,
        0x63, 0x64, 0x68, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x35, 0x32,
        0x31, 0x2c, 0x64, 0x69, 0x66, 0x66, 0x69, 0x65,
        0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e,
        0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x65,
        0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d,
        0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x2c, 0x64,
        0x69, 0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65,
        0x6c, 0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72,
        0x6f, 0x75, 0x70, 0x31, 0x36, 0x2d, 0x73, 0x68,
        0x61, 0x35, 0x31, 0x32, 0x2c, 0x64, 0x69, 0x66,
        0x66, 0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c, 0x6c,
        0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f, 0x75,
        0x70, 0x31, 0x38, 0x2d, 0x73, 0x68, 0x61, 0x35,
        0x31, 0x32, 0x2c, 0x64, 0x69, 0x66, 0x66, 0x69,
        0x65, 0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61,
        0x6e, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d,
        0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
        0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c, 0x64, 0x69,
        0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c,
        0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f,
        0x75, 0x70, 0x31, 0x34, 0x2d, 0x73, 0x68, 0x61,
        0x32, 0x35, 0x36, 0x2c, 0x64, 0x69, 0x66, 0x66,
        0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d,
        0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70,
        0x31, 0x34, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c,
        0x65, 0x78, 0x74, 0x2d, 0x69, 0x6e, 0x66, 0x6f,
        0x2d, 0x63, 0x00, 0x00, 0x01, 0x22, 0x65, 0x63,
        0x64, 0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35,
        0x36, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x2d, 0x76,
        0x30, 0x31, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x65,
        0x63, 0x64, 0x73, 0x61, 0x2d, 0x73, 0x68, 0x61,
        0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x33,
        0x38, 0x34, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x2d,
        0x76, 0x30, 0x31, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70,
        0x35, 0x32, 0x31, 0x2d, 0x63, 0x65, 0x72, 0x74,
        0x2d, 0x76, 0x30, 0x31, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73,
        0x68, 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74,
        0x70, 0x32, 0x35, 0x36, 0x2c, 0x65, 0x63, 0x64,
        0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d,
        0x6e, 0x69, 0x73, 0x74, 0x70, 0x33, 0x38, 0x34,
        0x2c, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73,
        0x68, 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74,
        0x70, 0x35, 0x32, 0x31, 0x2c, 0x73, 0x73, 0x68,
        0x2d, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39,
        0x2d, 0x63, 0x65, 0x72, 0x74, 0x2d, 0x76, 0x30,
        0x31, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x73, 0x73,
        0x68, 0x2d, 0x72, 0x73, 0x61, 0x2d, 0x63, 0x65,
        0x72, 0x74, 0x2d, 0x76, 0x30, 0x31, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x73, 0x73, 0x68, 0x2d, 0x65,
        0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x2c, 0x72,
        0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d,
        0x35, 0x31, 0x32, 0x2c, 0x72, 0x73, 0x61, 0x2d,
        0x73, 0x68, 0x61, 0x32, 0x2d, 0x32, 0x35, 0x36,
        0x2c, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73, 0x61,
        0x00, 0x00, 0x00, 0x8d, 0x63, 0x68, 0x61, 0x63,
        0x68, 0x61, 0x32, 0x30, 0x2d, 0x70, 0x6f, 0x6c,
        0x79, 0x31, 0x33, 0x30, 0x35, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x32, 0x38,
        0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61, 0x65, 0x73,
        0x31, 0x39, 0x32, 0x2d, 0x63, 0x74, 0x72, 0x2c,
        0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x63,
        0x74, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x32,
        0x38, 0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35, 0x36,
        0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x61, 0x65, 0x73, 0x31, 0x32, 0x38, 0x2d,
        0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73, 0x31,
        0x39, 0x32, 0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61,
        0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x63, 0x62,
        0x63, 0x00, 0x00, 0x00, 0x8d, 0x63, 0x68, 0x61,
        0x63, 0x68, 0x61, 0x32, 0x30, 0x2d, 0x70, 0x6f,
        0x6c, 0x79, 0x31, 0x33, 0x30, 0x35, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x32,
        0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61, 0x65,
        0x73, 0x31, 0x39, 0x32, 0x2d, 0x63, 0x74, 0x72,
        0x2c, 0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d,
        0x63, 0x74, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x31,
        0x32, 0x38, 0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35,
        0x36, 0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x32, 0x38,
        0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73,
        0x31, 0x39, 0x32, 0x2d, 0x63, 0x62, 0x63, 0x2c,
        0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x63,
        0x62, 0x63, 0x00, 0x00, 0x00, 0xd5, 0x75, 0x6d,
        0x61, 0x63, 0x2d, 0x36, 0x34, 0x2d, 0x65, 0x74,
        0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x75, 0x6d,
        0x61, 0x63, 0x2d, 0x31, 0x32, 0x38, 0x2d, 0x65,
        0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x32, 0x35, 0x36, 0x2d, 0x65, 0x74, 0x6d,
        0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
        0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d, 0x35,
        0x31, 0x32, 0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d,
        0x73, 0x68, 0x61, 0x31, 0x2d, 0x65, 0x74, 0x6d,
        0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
        0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x75, 0x6d, 0x61,
        0x63, 0x2d, 0x36, 0x34, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x31, 0x32,
        0x38, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d,
        0x32, 0x35, 0x36, 0x2c, 0x68, 0x6d, 0x61, 0x63,
        0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d, 0x35, 0x31,
        0x32, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73,
        0x68, 0x61, 0x31, 0x00, 0x00, 0x00, 0xd5, 0x75,
        0x6d, 0x61, 0x63, 0x2d, 0x36, 0x34, 0x2d, 0x65,
        0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x75,
        0x6d, 0x61, 0x63, 0x2d, 0x31, 0x32, 0x38, 0x2d,
        0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61,
        0x32, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x65, 0x74,
        0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d,
        0x35, 0x31, 0x32, 0x2d, 0x65, 0x74, 0x6d, 0x40,
        0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
        0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63,
        0x2d, 0x73, 0x68, 0x61, 0x31, 0x2d, 0x65, 0x74,
        0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x75, 0x6d,
        0x61, 0x63, 0x2d, 0x36, 0x34, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x31,
        0x32, 0x38, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x32, 0x35, 0x36, 0x2c, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d, 0x35,
        0x31, 0x32, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d,
        0x73, 0x68, 0x61, 0x31, 0x00, 0x00, 0x00, 0x1a,
        0x6e, 0x6f, 0x6e, 0x65, 0x2c, 0x7a, 0x6c, 0x69,
        0x62, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x7a, 0x6c,
        0x69, 0x62, 0x00, 0x00, 0x00, 0x1a, 0x6e, 0x6f,
        0x6e, 0x65, 0x2c, 0x7a, 0x6c, 0x69, 0x62, 0x40,
        0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
        0x63, 0x6f, 0x6d, 0x2c, 0x7a, 0x6c, 0x69, 0x62,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    char s_kexinit[] = { /* Server Key Exchange Init */
        0x00, 0x00, 0x06, 0x6c, 0x0a, 0x14, 0x2b, 0xc3,
        0x5c, 0x63, 0xa7, 0xaf, 0x61, 0x24, 0xc1, 0x88,
        0x32, 0xb0, 0x06, 0xa7, 0x91, 0x3c, 0x00, 0x00,
        0x00, 0xd4, 0x63, 0x75, 0x72, 0x76, 0x65, 0x32,
        0x35, 0x35, 0x31, 0x39, 0x2d, 0x73, 0x68, 0x61,
        0x32, 0x35, 0x36, 0x40, 0x6c, 0x69, 0x62, 0x73,
        0x73, 0x68, 0x2e, 0x6f, 0x72, 0x67, 0x2c, 0x65,
        0x63, 0x64, 0x68, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35,
        0x36, 0x2c, 0x65, 0x63, 0x64, 0x68, 0x2d, 0x73,
        0x68, 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74,
        0x70, 0x33, 0x38, 0x34, 0x2c, 0x65, 0x63, 0x64,
        0x68, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x2d, 0x6e,
        0x69, 0x73, 0x74, 0x70, 0x35, 0x32, 0x31, 0x2c,
        0x64, 0x69, 0x66, 0x66, 0x69, 0x65, 0x2d, 0x68,
        0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67,
        0x72, 0x6f, 0x75, 0x70, 0x2d, 0x65, 0x78, 0x63,
        0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x35, 0x36, 0x2c, 0x64, 0x69, 0x66,
        0x66, 0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c, 0x6c,
        0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f, 0x75,
        0x70, 0x2d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e,
        0x67, 0x65, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c,
        0x64, 0x69, 0x66, 0x66, 0x69, 0x65, 0x2d, 0x68,
        0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67,
        0x72, 0x6f, 0x75, 0x70, 0x31, 0x34, 0x2d, 0x73,
        0x68, 0x61, 0x31, 0x2c, 0x64, 0x69, 0x66, 0x66,
        0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d,
        0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70,
        0x31, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x00, 0x00,
        0x00, 0x2f, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73,
        0x61, 0x2c, 0x73, 0x73, 0x68, 0x2d, 0x64, 0x73,
        0x73, 0x2c, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d,
        0x73, 0x68, 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73,
        0x74, 0x70, 0x32, 0x35, 0x36, 0x2c, 0x73, 0x73,
        0x68, 0x2d, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31,
        0x39, 0x00, 0x00, 0x00, 0xe9, 0x61, 0x65, 0x73,
        0x31, 0x32, 0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c,
        0x61, 0x65, 0x73, 0x31, 0x39, 0x32, 0x2d, 0x63,
        0x74, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35,
        0x36, 0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61, 0x72,
        0x63, 0x66, 0x6f, 0x75, 0x72, 0x32, 0x35, 0x36,
        0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f, 0x75, 0x72,
        0x31, 0x32, 0x38, 0x2c, 0x61, 0x65, 0x73, 0x31,
        0x32, 0x38, 0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35,
        0x36, 0x2d, 0x67, 0x63, 0x6d, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x63, 0x68, 0x61, 0x63, 0x68, 0x61,
        0x32, 0x30, 0x2d, 0x70, 0x6f, 0x6c, 0x79, 0x31,
        0x33, 0x30, 0x35, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x61, 0x65, 0x73, 0x31, 0x32, 0x38, 0x2d, 0x63,
        0x62, 0x63, 0x2c, 0x33, 0x64, 0x65, 0x73, 0x2d,
        0x63, 0x62, 0x63, 0x2c, 0x62, 0x6c, 0x6f, 0x77,
        0x66, 0x69, 0x73, 0x68, 0x2d, 0x63, 0x62, 0x63,
        0x2c, 0x63, 0x61, 0x73, 0x74, 0x31, 0x32, 0x38,
        0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73,
        0x31, 0x39, 0x32, 0x2d, 0x63, 0x62, 0x63, 0x2c,
        0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x63,
        0x62, 0x63, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f,
        0x75, 0x72, 0x2c, 0x72, 0x69, 0x6a, 0x6e, 0x64,
        0x61, 0x65, 0x6c, 0x2d, 0x63, 0x62, 0x63, 0x40,
        0x6c, 0x79, 0x73, 0x61, 0x74, 0x6f, 0x72, 0x2e,
        0x6c, 0x69, 0x75, 0x2e, 0x73, 0x65, 0x00, 0x00,
        0x00, 0xe9, 0x61, 0x65, 0x73, 0x31, 0x32, 0x38,
        0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61, 0x65, 0x73,
        0x31, 0x39, 0x32, 0x2d, 0x63, 0x74, 0x72, 0x2c,
        0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x63,
        0x74, 0x72, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f,
        0x75, 0x72, 0x32, 0x35, 0x36, 0x2c, 0x61, 0x72,
        0x63, 0x66, 0x6f, 0x75, 0x72, 0x31, 0x32, 0x38,
        0x2c, 0x61, 0x65, 0x73, 0x31, 0x32, 0x38, 0x2d,
        0x67, 0x63, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x61, 0x65, 0x73, 0x32, 0x35, 0x36, 0x2d, 0x67,
        0x63, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x63,
        0x68, 0x61, 0x63, 0x68, 0x61, 0x32, 0x30, 0x2d,
        0x70, 0x6f, 0x6c, 0x79, 0x31, 0x33, 0x30, 0x35,
        0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
        0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x61, 0x65, 0x73,
        0x31, 0x32, 0x38, 0x2d, 0x63, 0x62, 0x63, 0x2c,
        0x33, 0x64, 0x65, 0x73, 0x2d, 0x63, 0x62, 0x63,
        0x2c, 0x62, 0x6c, 0x6f, 0x77, 0x66, 0x69, 0x73,
        0x68, 0x2d, 0x63, 0x62, 0x63, 0x2c, 0x63, 0x61,
        0x73, 0x74, 0x31, 0x32, 0x38, 0x2d, 0x63, 0x62,
        0x63, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x39, 0x32,
        0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73,
        0x32, 0x35, 0x36, 0x2d, 0x63, 0x62, 0x63, 0x2c,
        0x61, 0x72, 0x63, 0x66, 0x6f, 0x75, 0x72, 0x2c,
        0x72, 0x69, 0x6a, 0x6e, 0x64, 0x61, 0x65, 0x6c,
        0x2d, 0x63, 0x62, 0x63, 0x40, 0x6c, 0x79, 0x73,
        0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x75,
        0x2e, 0x73, 0x65, 0x00, 0x00, 0x01, 0x92, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2d,
        0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61,
        0x31, 0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36,
        0x34, 0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x31,
        0x32, 0x38, 0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d,
        0x73, 0x68, 0x61, 0x32, 0x2d, 0x32, 0x35, 0x36,
        0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x35, 0x31, 0x32, 0x2d, 0x65,
        0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70, 0x65,
        0x6d, 0x64, 0x31, 0x36, 0x30, 0x2d, 0x65, 0x74,
        0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x2d,
        0x39, 0x36, 0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d,
        0x6d, 0x64, 0x35, 0x2d, 0x39, 0x36, 0x2d, 0x65,
        0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2c,
        0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61,
        0x31, 0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36,
        0x34, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x75, 0x6d,
        0x61, 0x63, 0x2d, 0x31, 0x32, 0x38, 0x40, 0x6f,
        0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63,
        0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d,
        0x73, 0x68, 0x61, 0x32, 0x2d, 0x32, 0x35, 0x36,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x35, 0x31, 0x32, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70, 0x65,
        0x6d, 0x64, 0x31, 0x36, 0x30, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d,
        0x64, 0x31, 0x36, 0x30, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68,
        0x61, 0x31, 0x2d, 0x39, 0x36, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2d, 0x39,
        0x36, 0x00, 0x00, 0x01, 0x92, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2d, 0x65, 0x74,
        0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
        0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x2d,
        0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36, 0x34, 0x2d,
        0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e,
        0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
        0x75, 0x6d, 0x61, 0x63, 0x2d, 0x31, 0x32, 0x38,
        0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x65,
        0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x35, 0x31, 0x32, 0x2d, 0x65, 0x74, 0x6d,
        0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
        0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64,
        0x31, 0x36, 0x30, 0x2d, 0x65, 0x74, 0x6d, 0x40,
        0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
        0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63,
        0x2d, 0x73, 0x68, 0x61, 0x31, 0x2d, 0x39, 0x36,
        0x2d, 0x65, 0x74, 0x6d, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64,
        0x35, 0x2d, 0x39, 0x36, 0x2d, 0x65, 0x74, 0x6d,
        0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
        0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2c, 0x68, 0x6d,
        0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c,
        0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36, 0x34, 0x40,
        0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
        0x63, 0x6f, 0x6d, 0x2c, 0x75, 0x6d, 0x61, 0x63,
        0x2d, 0x31, 0x32, 0x38, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68,
        0x61, 0x32, 0x2d, 0x32, 0x35, 0x36, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x35, 0x31, 0x32, 0x2c, 0x68, 0x6d, 0x61,
        0x63, 0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64,
        0x31, 0x36, 0x30, 0x2c, 0x68, 0x6d, 0x61, 0x63,
        0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64, 0x31,
        0x36, 0x30, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
        0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
        0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31,
        0x2d, 0x39, 0x36, 0x2c, 0x68, 0x6d, 0x61, 0x63,
        0x2d, 0x6d, 0x64, 0x35, 0x2d, 0x39, 0x36, 0x00,
        0x00, 0x00, 0x15, 0x6e, 0x6f, 0x6e, 0x65, 0x2c,
        0x7a, 0x6c, 0x69, 0x62, 0x40, 0x6f, 0x70, 0x65,
        0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
        0x00, 0x00, 0x00, 0x15, 0x6e, 0x6f, 0x6e, 0x65,
        0x2c, 0x7a, 0x6c, 0x69, 0x62, 0x40, 0x6f, 0x70,
        0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
        0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    char c_dhkex[] = { /* Client DH Key Exchange */
        0x00, 0x00, 0x00, 0x2c, 0x06, 0x1e, 0x00, 0x00,
        0x00, 0x20, 0x67, 0x95, 0x9b, 0xa6, 0x5f, 0xa3,
        0xd2, 0x07, 0xda, 0xba, 0x17, 0xef, 0x54, 0x5a,
        0xa4, 0x42, 0x8b, 0x3c, 0x11, 0xdf, 0x30, 0x68,
        0x81, 0xdc, 0x85, 0xcf, 0xbe, 0xe3, 0xd8, 0xa2,
        0x9b, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    char s_dhkex_newkeys[] = { /* Server DH Key Exchange, Server New Keys */
        0x00, 0x00, 0x01, 0x04, 0x09, 0x1f, 0x00, 0x00,
        0x00, 0x68, 0x00, 0x00, 0x00, 0x13, 0x65, 0x63,
        0x64, 0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35,
        0x36, 0x00, 0x00, 0x00, 0x08, 0x6e, 0x69, 0x73,
        0x74, 0x70, 0x32, 0x35, 0x36, 0x00, 0x00, 0x00,
        0x41, 0x04, 0x29, 0xeb, 0x34, 0xde, 0xa6, 0x63,
        0x63, 0xe5, 0x98, 0x41, 0xc8, 0x6f, 0x21, 0x44,
        0x33, 0x80, 0xa3, 0x6e, 0xc7, 0x07, 0x03, 0xa4,
        0x7b, 0x93, 0x89, 0x13, 0x3b, 0xff, 0xb3, 0x2a,
        0x04, 0x65, 0x25, 0xaf, 0x09, 0x74, 0x9f, 0x27,
        0x74, 0x8c, 0x2e, 0xca, 0x48, 0x52, 0xa0, 0xc7,
        0x65, 0xbb, 0x7a, 0x22, 0xc4, 0x3d, 0x75, 0x34,
        0x9b, 0xb5, 0x9c, 0x90, 0x62, 0x01, 0xe5, 0x47,
        0x7d, 0x47, 0x00, 0x00, 0x00, 0x20, 0x3b, 0x64,
        0x88, 0x52, 0xd5, 0x54, 0x23, 0xc5, 0xb5, 0x28,
        0xd3, 0x62, 0x0b, 0xd7, 0x65, 0x0f, 0x21, 0x99,
        0xf6, 0x23, 0xa6, 0x1c, 0x70, 0xaf, 0x8d, 0x37,
        0xb1, 0xdf, 0xe0, 0x9b, 0x3a, 0x7f, 0x00, 0x00,
        0x00, 0x65, 0x00, 0x00, 0x00, 0x13, 0x65, 0x63,
        0x64, 0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32,
        0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35,
        0x36, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
        0x21, 0x00, 0xb6, 0xfb, 0x25, 0xd4, 0xdf, 0x1a,
        0x3b, 0xaa, 0x93, 0x05, 0x8c, 0x83, 0xf4, 0x0b,
        0x67, 0x98, 0x02, 0x96, 0x89, 0x56, 0x92, 0x4b,
        0xc5, 0x6d, 0x17, 0x9b, 0xc6, 0xfc, 0xd1, 0xce,
        0xef, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0xd1,
        0xf6, 0xce, 0xf1, 0x98, 0x28, 0xa4, 0x43, 0xa1,
        0x1f, 0x75, 0xeb, 0x65, 0xb1, 0x99, 0x81, 0x59,
        0x1f, 0xc6, 0x4e, 0xac, 0x8f, 0x22, 0x2a, 0xbb,
        0xb7, 0x08, 0x6c, 0xa2, 0xd9, 0x1a, 0xa6, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x0c, 0x0a, 0x15, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    char c_newkeys[] = { /* Client New Keys */
        0x00, 0x00, 0x00, 0x0c, 0x0a, 0x15, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

    ssh_init(&cli);
    ssh_update(cli, NULL, c_protocol, sizeof(c_protocol), 1);
    ssh_update(cli, NULL, c_kexinit, sizeof(c_kexinit), 1);
    ssh_update(cli, NULL, c_dhkex, sizeof(c_dhkex), 1);
    ssh_update(cli, NULL, c_newkeys, sizeof(c_newkeys), 1);

    ssh_init(&srv);
    ssh_update(srv, NULL, s_protocol, sizeof(s_protocol), 1);
    ssh_update(srv, NULL, s_kexinit, sizeof(s_kexinit), 1);
    ssh_update(srv, NULL, s_dhkex_newkeys, sizeof(s_dhkex_newkeys), 1);
    ssh_process(cli, srv);

    if ((strcmp_s(cli->protocol, 50, "SSH-2.0-OpenSSH_7.4p1 Debian-10", &cmp_ind) != EOK)  || cmp_ind != 0) {
        joy_log_err("failure: cli protocol");
        num_fails++;
    }

    if ((strcmp_s(srv->protocol, 50, "SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8", &cmp_ind) != EOK) || cmp_ind != 0) {
        joy_log_err("failure: srv protocol");
        num_fails++;
    }

    if ((strcmp_s(cli->kex_algo, 50, "curve25519-sha256@libssh.org", &cmp_ind) != EOK) || cmp_ind != 0) {
        joy_log_err("failure: cli kex_algo");
        num_fails++;
    }

    if ((strcmp_s(srv->kex_algo, 50, "curve25519-sha256@libssh.org", &cmp_ind) != EOK) || cmp_ind != 0) {
        joy_log_err("failure: srv kex_algo");
        num_fails++;
    }

    if ( !cli->newkeys) {
        joy_log_err("failure: cli newkeys");
        num_fails++;
    }

    if ( ! cli->newkeys) {
        joy_log_err("failure: cli newkeys");
        num_fails++;
    }

    ssh_delete(&cli);
    ssh_delete(&srv);
    return num_fails;
}

void ssh_unit_test() {
    int num_fails = 0;

    joy_log_info("\n******************************");
    joy_log_info("SSH Unit Test starting...");

    num_fails += ssh_test_handshake();

    if (num_fails) {
        joy_log_info("Finished - # of failures: %d", num_fails);
    } else {
        joy_log_info("Finished - success");
    }
    joy_log_info("******************************\n");
}

